package com.fs.sys.service;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.fs.common.AppConstants;
import com.fs.common.ErrorCodes;
import com.fs.common.base.service.CacheService;
import com.fs.common.exception.AppException;
import com.fs.sys.bean.dto.SysConfigDTO;
import com.fs.sys.bean.query.SysConfigQuery;
import com.fs.sys.entityservice.SysConfigEntityService;
import jakarta.annotation.PostConstruct;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 短信服务<br/>
 * 调用短信实际发送服务进行短信发送
 *
 * @author LiuQi 2025/6/19-8:52
 * @version V1.0
 **/
@Service
@Slf4j
public class PhoneMessageService {
    @Autowired
    private Set<PhoneMessageSendService> sendServices;

    @Autowired
    private SysConfigEntityService sysConfigEntityService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private KaptchaService kaptchaService;

    private final Map<String, PhoneMessageSendService> sendServiceMap = new HashMap<>(16);

    @PostConstruct
    public void init() {
        if (CollectionUtils.isEmpty(sendServices)) {
            return;
        }
        sendServices.forEach(service -> sendServiceMap.put(service.getChannel(), service));
    }

    /**
     * 校验验证码是否准确
     * @param phone 手机号
     * @param code 验证码
     */
    public void verifyCode(String phone, String code) {
        String cachedCode = cacheService.getAndDelete("verifyCode:" + phone);
        if (!StringUtils.equals(code, cachedCode)) {
            throw AppException.of("短信验证码无效");
        }
    }

    /**
     * 发送短信验证码
     *
     * @param phone     手机号
     * @param imageKey 图形验证码key
     * @param imageCode 图形验证码
     */
    public void sendVerifyCode(String phone, String imageKey, String imageCode) {
        // 先校验图形验证码
        kaptchaService.checkCode(imageKey, imageCode);

        String code = generateCode();
        cacheService.cache("verifyCode:" + phone, code);
        log.debug("手机号：{}，验证码：{}", phone, code);

        // 短信配置
        MsgConfig config = this.getMsgConfig();
        if (null == config || StringUtils.isBlank(config.getChannel())
                || StringUtils.isBlank(config.getTemplate())
                || StringUtils.isBlank(config.getKey())
                || StringUtils.isBlank(config.getSecret())) {
            throw AppException.of(ErrorCodes.BASE_MESSAGE_NOT_CONFIG);
        }

        String template = config.getTemplate();
        String channel = config.getChannel();
        PhoneMessageSendService sendService = sendServiceMap.get(channel);
        if (null == sendService) {
            throw AppException.of(ErrorCodes.BASE_MESSAGE_CHANNEL_NOT_SUPPORTED);
        }

        Map<String, Object> params = new HashMap<>(16);
        params.put("code", code);
        sendService.send(config, phone, template, params);
    }

    /**
     * 生成数字验证码
     * @return 数字验证码
     */
    private String generateCode() {
        return String.valueOf(RandomUtil.randomInt(100000, 999999));
    }

    /**
     * 获取短信配置
     * @return 短信配置，可为空
     */
    private MsgConfig getMsgConfig() {
        SysConfigQuery query = new SysConfigQuery();
        query.setTenantId(AppConstants.SUPER_TENANT);
        query.setCode("sysConfig");
        SysConfigDTO dto = sysConfigEntityService.findOne(query).orElse(null);
        if (null == dto) {
            return null;
        }

        String value = dto.getValue();
        JSONObject obj = JSON.parseObject(value);
        return obj.getObject("message", MsgConfig.class);
    }

    @Data
    public static class MsgConfig {
        /**
         * 渠道
         */
        private String channel;

        /**
         * 验证码模板
         */
        private String template;

        private String key;

        private String secret;

        private String sign;
    }
}
